package com.xyy.saas.payment.adpater.aliPayHandler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.channel.setting.ChannelSettingManager;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.PayBo;
import com.xyy.saas.payment.cores.bo.TransactionResultBO;
import com.xyy.saas.payment.cores.enums.PayModeEnum;
import com.xyy.saas.payment.cores.enums.PayResultModeEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.TransactionStatusEnum;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.dao.model.AlipaySettings;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.service.RefundOrderService;
import com.xyy.saas.payment.util.AmountCastUtil;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.JSONUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author zhangwenxiong
 * @Date 2020/12/1 11:05
 * @Version 1.0
 */
@Component
public class AliH5PayHandler implements AliPayChannelHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(AliH5PayHandler.class);

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private ChannelSettingManager channelSettingManager;

    @Autowired
    private RefundOrderService refundOrderService;

    @Override
    public String getMethod() {
        return PayModeEnum.H5.getMode();
    }

    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        PayBo appPayBo = redisClient.get(payDto.getPayMode()+payDto.getPayNo()+"aliPay", PayBo.class);
        if(appPayBo != null) {
            LOGGER.info("AliH5PayHandler pay end get from redis appPayBo:{}", JSON.toJSONString(appPayBo));
            return appPayBo;
        }
        AlipayClient client = channelSettingManager.getAlipayClient(payDto.getReceiverId(), payDto.getBusinessOrderType(), payDto.getPayMode());
        LOGGER.info("AliH5PayHandler pay begin payDto:{}",JSON.toJSONString(payDto));
        PayBo payBo = new PayBo();
        //获取请求体
        AlipayTradeWapPayRequest alipayRequest =  new  AlipayTradeWapPayRequest();
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setOutTradeNo(payDto.getPayNo());
        model.setTotalAmount(new BigDecimal(payDto.getAmount()).divide(new BigDecimal(100),2,BigDecimal.ROUND_UNNECESSARY).toString());
        model.setSubject(payDto.getSubject());
        model.setBody(payDto.getBody());
        if(payDto.getMap().get("quitUrl") != null) {
            model.setQuitUrl(payDto.getMap().get("quitUrl").toString());
        }
        model.setTimeoutExpress(payDto.getMap().get("timeoutExpress") == null? null:payDto.getMap().get("timeoutExpress").toString());
        alipayRequest.setNotifyUrl(ApolloUtil.getProperty("alipay.notifyUrl.pay.direct"));
        alipayRequest.setBizModel(model);
        if(payDto.getMap().get("cashierJumpUrl") != null) {
            alipayRequest.setReturnUrl(payDto.getMap().get("cashierJumpUrl").toString());
        }
        //调用支付宝接口
        try {
            if(payDto.getPayResultMode() != null && PayResultModeEnum.BASIC.getMode().equals(payDto.getPayResultMode())){
                AlipayTradeWapPayResponse response = client.pageExecute(alipayRequest);
                LOGGER.info("AliH5PayHandler alipayRequest basic:{}, response:{}", JSON.toJSONString(alipayRequest), JSON.toJSONString(response.getBody()));
                if(!response.isSuccess()) {
                    LOGGER.error("AliH5PayHandler pay fail PayNo:{}", payDto.getPayNo());
                    throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, response.getMsg());
                }
                payBo.setPayLoad(response.getBody());
            } else if (payDto.getPayResultMode() != null && PayResultModeEnum.GET.getMode().equals(payDto.getPayResultMode())){
                AlipayTradeWapPayResponse response = client.pageExecute(alipayRequest, "GET");
                LOGGER.info("AliH5PayHandler alipayRequest get:{}, response:{}", JSON.toJSONString(alipayRequest), JSON.toJSONString(response.getBody()));
                if(!response.isSuccess()) {
                    LOGGER.error("AliH5PayHandler pay fail PayNo:{}", payDto.getPayNo());
                    throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, response.getMsg());
                }
                payBo.setPayLoad(response.getBody());
            } else {
                AlipayTradeWapPayResponse response = client.pageExecute(alipayRequest, "GET");
                LOGGER.info("AliH5PayHandler alipayRequest map:{}, response:{}", JSON.toJSONString(alipayRequest), JSON.toJSONString(response.getBody()));
                if(!response.isSuccess()) {
                    LOGGER.error("AliH5PayHandler pay fail PayNo:{}", payDto.getPayNo());
                    throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, response.getMsg());
                }
                Map<String, Object> map = getParameter(response.getBody());
                map.put("channel_url", "https://openapi.alipay.com/gateway.do");
                payBo.setPayLoad(map.toString());
            }
            payBo.setPayNO(payDto.getPayNo());
            LOGGER.info("AliH5PayHandler pay end PayNo:{} payLoad:{}", payDto.getPayNo(),JSONObject.toJSONString(payBo));
        } catch (AlipayApiException e) {
            LOGGER.error("AliH5PayHandler pay ali error payNo:{}, exception:{}", payDto.getPayNo(),e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
        }
        //默认超时时间,如果业务有传超时时间就按照他们的时间+5400秒
        Integer expire = 5400;
        if (null != payDto.getTimeExpire() && payDto.getTimeExpire().after(new Date())) {
            expire = DateUtil.getSecondsFromToday(payDto.getTimeExpire()).intValue() + expire;
        }
        redisClient.set(payDto.getPayMode()+payDto.getPayNo()+"aliPay", payBo,expire);
        return payBo;
    }

    public static Map<String, Object> getParameter(String url) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            final String charset = "utf-8";
//            url = URLDecoder.decode(url, charset);
            if (url.indexOf('?') != -1) {
                final String contents = url.substring(url.indexOf('?') + 1);
                String[] keyValues = contents.split("&");
                for (int i = 0; i < keyValues.length; i++) {
                    String key = keyValues[i].substring(0, keyValues[i].indexOf("="));
                    String value = keyValues[i].substring(keyValues[i].indexOf("=") + 1);
                    value = URLDecoder.decode(value, charset);
                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    @Override
    public TransactionResultBO queryPay(TransactionParam param) throws PaymentException {
        LOGGER.info("AliAppPayHandler queryPay begin param:{}", JSONUtils.toJSON(param));
        AlipayClient client = channelSettingManager.getAlipayClient(param.getAccountId(), param.getBusinessOrderType(), param.getPayMode());
        AlipaySettings alipaySettings = channelSettingManager.getAlipaySettings(param.getAccountId(), param.getBusinessOrderType(), param.getPayMode());
        if (null == client){
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        //获取请求体
        AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
        TransactionResultBO resultBO = new TransactionResultBO();
        resultBO.setMerchantId(alipaySettings.getAlipayAppId());
        //开始组装支付宝APP接口需要的参数
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        model.setOutTradeNo(param.getTradeNo());
        alipayRequest.setBizModel(model);
        //返回支付宝订单信息(预处理)
        try {
            AlipayTradeQueryResponse response = client.execute(alipayRequest);
            if(response.isSuccess()){
                resultBO.setTradeNo(param.getTradeNo());
                resultBO.setAmount(AmountCastUtil.castYuan2Fen(response.getTotalAmount()));
                resultBO.setChannelChargeNo(response.getTradeNo());
                switch (response.getTradeStatus()) {
                    case "TRADE_SUCCESS":
                        resultBO.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                        resultBO.setSuccessTime(DateUtil.dateToLong(response.getSendPayDate()));
                        break;
                    case "WAIT_BUYER_PAY":
                        resultBO.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                        break;
                    default:
                        resultBO.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                        break;
                }
                return resultBO;
            }
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR,response.getMsg());
        } catch (AlipayApiException e) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode());
        } catch (Exception e) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public TransactionResultBO queryRefund(TransactionParam param) throws PaymentException {
        LOGGER.info("AliAppPayHandler queryPay begin param:{}", JSONUtils.toJSON(param));
        AlipayClient client = channelSettingManager.getAlipayClient(param.getAccountId(), param.getBusinessOrderType(), param.getPayMode());
        if (null == client){
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }

        RefundOrder refundOrder = refundOrderService.selectByRefundNo(param.getTradeNo());
        if(null == refundOrder) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        TransactionResultBO resultBO = new TransactionResultBO();
        resultBO.setTradeNo(param.getTradeNo());

        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", refundOrder.getPayNo());
        bizContent.put("out_request_no", refundOrder.getRefundNo());
        request.setBizContent(bizContent.toString());

        try {
            AlipayTradeFastpayRefundQueryResponse response = client.execute(request);
            if(response.isSuccess()){
                resultBO.setChannelChargeNo(response.getTradeNo());
                resultBO.setSuccessTime(DateUtil.dateToLong(response.getGmtRefundPay()));
                resultBO.setAmount(AmountCastUtil.castYuan2Fen(response.getRefundAmount()));
                if("REFUND_SUCCESS".equals(response.getRefundStatus())) {
                    resultBO.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                }else{
                    resultBO.setStatus(TransactionStatusEnum.FAIL.getCode());
                }
                return resultBO;
            } else {
                resultBO.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                return resultBO;
            }
        } catch (AlipayApiException e) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode());
        } catch (Exception e) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }
}
